﻿//
// Created by Grishul Eugeny, Shakola Igor
//
// See license in License.txt
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.IO;
using System.ComponentModel;
using NCommander.Core;
using System.Text.RegularExpressions;

namespace NCommander.NodeProviders
{
	[ShortName( "registry" )]
	internal sealed class RegistryProvider : NodeProvider
	{
		internal class RegistryKeyNode : Node
		{
			public RegistryKey Key;
			public RegistryKeyNode ParentNode;

			public override string Path
			{
				get { return Key.ToString(); }
			}

			public override Node Parent { get { return ParentNode; } }
			public override NodeType Type { get { return NodeType.Internal; } }

			public override bool Equals( object obj )
			{
				if( obj == null ) return false;
				if( !( obj is RegistryKeyNode ) ) return false;

				return Key.Equals( ( ( RegistryKeyNode ) obj ).Key );
			}
		}

		internal class RegistryValueNode : Node
		{
			public string Name;
			public object Value;
			public RegistryKeyNode ParentNode;

			public RegistryValueKind Kind;

			public override string Path
			{
				get { return Name; }
			}	

			public override Node Parent { get { return ParentNode; } }
			public override NodeType Type { get { return NodeType.External; } }
		}

		internal class RegistryUpNode : FollowUpNode
		{
			public RegistryKeyNode Node;
			public override Node Parent { get { return Node.Parent; } }
		}

		private static readonly RegistryKey[] _rootKeys =
		{ 
			  Registry.CurrentConfig
			, Registry.ClassesRoot
			, Registry.CurrentUser
			, Registry.DynData
			, Registry.LocalMachine
			, Registry.PerformanceData
			, Registry.Users
		};

		private RegistryKeyNode _currentNode = new RegistryKeyNode() { Key = Registry.CurrentUser };

		#region NodeProvider Members

		public override Node[] Roots
		{
			get { return _rootKeys.Select( x => new RegistryKeyNode() { Key = x } ).ToArray(); }
		}

		public override Node CurrentNode
		{
			get { return _currentNode; }
			set
			{
				var upnode = value as RegistryUpNode;

				if( upnode != null )
					_currentNode = upnode.Node;
				else
					_currentNode = ( RegistryKeyNode ) value;

				InvokeRefresh();
			}
		}

		public override IEnumerable<Node> GetNodes()
		{
			if( !_rootKeys.Contains( _currentNode.Key ) )
				yield return new RegistryUpNode() { Node = ( RegistryKeyNode ) _currentNode.Parent };

			foreach( var name in _currentNode.Key.GetSubKeyNames() )
				yield return new RegistryKeyNode() { Key = _currentNode.Key.OpenSubKey( name ), ParentNode = _currentNode };

			foreach( var name in _currentNode.Key.GetValueNames() )
				yield return new RegistryValueNode() { ParentNode = _currentNode, Kind = _currentNode.Key.GetValueKind( name ), Name = name, Value = _currentNode.Key.GetValue( name ) };
		}

		public override Node GetNode( string value )
		{
			RegistryKeyNode result;

			var pathParts = value.Split( Path.DirectorySeparatorChar );

			if( pathParts.Length > 1 )
				result = new RegistryKeyNode() { Key = DetectRootKey( pathParts[1] ).OpenSubKey( "" ) };
			else
				result = new RegistryKeyNode() { Key = DetectRootKey( pathParts[1] ) };

			return result;
		}

		public override void OnExecute( Node node )
		{
			if( node.Type == NodeType.Internal )
			{
				CurrentNode = node;
			}
		}

		#endregion

		private RegistryKey DetectRootKey( String keyName )
		{
			switch( keyName )
			{
				case "HKCR":
				case "HKEY_CLASSES_ROOT":
					return Registry.ClassesRoot;

				case "HKLM":
				case "HKEY_LOCAL_MACHINE":
					return Registry.LocalMachine;

				case "HKCU":
				case "HKEY_CURRENT_USER":
					return Registry.CurrentUser;

				case "HKU":
				case "HKEY_USERS":
					return Registry.Users;

				case "HKCC":
				case "HKEY_CURRENT_CONFIG":
					return Registry.CurrentConfig;

				default:
					throw new ArgumentException( keyName );
			}
		}

		public override string ToString()
		{
			return "Registry";
		}

		


		#region Registry processing

		public void CreateSubKey()
		{
			RegistryKeyNode node = CurrentNode.Parent as RegistryKeyNode;

			bool rootNode = node == null ? true : false;
			if( rootNode ) node = CurrentNode as RegistryKeyNode;

			int defaultIndex = 1;
			var defaultNameNodes = node.Key.GetSubKeyNames().Where( n => Regex.Match( n, "New Key #[0-9]*$" ).Success );
			foreach( String name in defaultNameNodes )
			{
				int currentIndex = Convert.ToInt32( name.Substring( name.IndexOf( '#' ) + 1 ) );

				if( currentIndex > defaultIndex ) defaultIndex = currentIndex;
				else if( currentIndex == defaultIndex ) defaultIndex++;
			}

			string defaultName = String.Format( "New Key #{0}", defaultIndex );

			if( rootNode )
				node.Key.CreateSubKey( defaultName );
			else
				node.Key.OpenSubKey( CurrentNode.Path.Split( Path.DirectorySeparatorChar ).Last(), true ).CreateSubKey( defaultName );

			InvokeRefresh();
		}

		public void DeleteSubKey( Node nodeToDelete )
		{
			RegistryKeyNode node = CurrentNode.Parent as RegistryKeyNode;
			if( node == null )
				( CurrentNode as RegistryKeyNode ).Key.DeleteSubKey( nodeToDelete.Path.Split( Path.DirectorySeparatorChar ).Last() );
			else
				node.Key.OpenSubKey( CurrentNode.Path.Split( Path.DirectorySeparatorChar ).Last(), true ).DeleteSubKey( nodeToDelete.Path.Split( Path.DirectorySeparatorChar ).Last() );

			InvokeRefresh();
		}

		public void RenameSubKey( Node nodeToRename, String newName )
		{
			RenameSubKey( ( nodeToRename.Parent as RegistryKeyNode ).Key, nodeToRename.Path.Split( Path.DirectorySeparatorChar ).Last(), newName );
		}

		private void RenameSubKey( RegistryKey parentKey, string subKeyName, string newSubKeyName )
		{
			CopyKey( parentKey, subKeyName, newSubKeyName );
			parentKey.DeleteSubKeyTree( subKeyName );
		}

		private void CopyKey( RegistryKey parentKey, string keyNameToCopy, string newKeyName )
		{
			RecurseCopyKey( parentKey.OpenSubKey( keyNameToCopy ), parentKey.CreateSubKey( newKeyName ) );
		}

		private void RecurseCopyKey( RegistryKey sourceKey, RegistryKey destinationKey )
		{
			//copy all the values
			foreach( string name in sourceKey.GetValueNames() )
				destinationKey.SetValue( name, sourceKey.GetValue( name ), sourceKey.GetValueKind( name ) );

			foreach( string sourceSubKeyName in sourceKey.GetSubKeyNames() )
				RecurseCopyKey( sourceKey.OpenSubKey( sourceSubKeyName ), destinationKey.CreateSubKey( sourceSubKeyName ) );
		}

		#endregion
	}
}
